package org.vacoor.xqq.ui.chat;

import org.vacoor.nothing.ui.util.WindowMoveHandler;
import org.vacoor.xqq.core.bean.Buddy;
import org.vacoor.xqq.core.bean.BuddyPair;
import org.vacoor.xqq.core.bean.Peer;
import org.vacoor.xqq.core.mod.client.impl.WebQQClient;
import org.vacoor.xqq.core.msg.MessageElement;
import org.vacoor.xqq.core.msg.MessageStyle;
import org.vacoor.xqq.core.msg.SendableMessage;
import org.vacoor.xqq.core.msg.impl.SimpleMessage;
import org.vacoor.xqq.core.poll.impl.AbstractPollReply;
import org.vacoor.xqq.core.util.ImageResources;
import org.vacoor.xqq.ui.comp.richeditor.*;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceChooser;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceInputMonitor;
import org.vacoor.xqq.ui.comp.richeditor.chooser.FaceUtil;
import org.vacoor.xqq.ui.comp.richeditor.chooser.StyleChooser;
import org.vacoor.xqq.ui.i.Chat2;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 聊天面板
 *
 * @author: vacoor
 */
public abstract class ChatPanel2<T extends Peer> extends JPanel implements Chat2 {
    private static final String DATE_FORMAT = "hh:mm:ss a";
    private static final int FONT_OFFSET = 2;  // 总是比系统字号小, 稍微矫正下
    private static final Color DEFAULT_TITLE_PANEL_BACKGROUND = new Color(0xE0EFF6);
    private static final Color DEFAULT_MID_TOOLBAR_BACKGROUND = new Color(0xE0EFF6);
    private static final Color DEFAULT_CONTENT_BORDER_COLOR = new Color(0xD4D6D3);
    private static final String DEFAULT_HELP_TEXT = "什么都没有";

    private static final ImageIcon styleButtonIcon = ImageResources.getIcon("images/midtoolbar/aio_quickbar_font.png");
    private static final ImageIcon faceButtonIcon = ImageResources.getIcon("images/midtoolbar/aio_quickbar_face.png");

    private AvatarPanel avatarPanel;        // 顶部头像标题

    private JSplitPane contentPane;         // 内容区域
    private RichEditor outputEditor;        // 输出编辑器
    private RichEditor inputEditor;         // 输入编辑器

    private JToggleButton styleBtn;         // 样式按钮
    private JToggleButton faceBtn;          // 表情按钮

    private StyleChooser styleChooser;      // 样式选择器
    private FaceChooser faceChooser;        // 表情选择器

    private String helpText = DEFAULT_HELP_TEXT;
    private JButton helpBtn;

    private Handler handler;

    protected final T peer;

    public ChatPanel2(T peer) {
        this.peer = peer;
        init();
        avatarPanel.initDataAndRenderer(peer);

        new FaceInputMonitor(inputEditor.getTextPane());
        new WindowMoveHandler().bindTo(avatarPanel);
    }

    private void init() {
        this.setLayout(new BorderLayout(0, 0));

        // 顶部标题栏
        avatarPanel = new AvatarPanel();
        avatarPanel.setOpaque(true);
        avatarPanel.setBackground(DEFAULT_TITLE_PANEL_BACKGROUND);
        avatarPanel.setBorder(BorderFactory.createMatteBorder(5, 10, 4, 0, new Color(255, 255, 255, 0)));

        // 内容区域
        contentPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        /*-
         * splitPane.setDividerLocation(0.6) 只在组件可见时有效
         * 添加 ComponentListener 可见时不会派发show事件
         * (因为JComponent visible 默认为true, 调用setVisible()才会)
         * 但是显示时候会派发 resize
         * 这里使用 setResizeWeight
         */
        contentPane.setResizeWeight(0.65);
        contentPane.setOneTouchExpandable(false);
        contentPane.setDividerSize(1);
        contentPane.setOpaque(false);
        contentPane.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, DEFAULT_CONTENT_BORDER_COLOR));

        // 内容区域输出
        final JPanel outPanel = new JPanel(new BorderLayout(0, 0));
        outPanel.setOpaque(false);

        outputEditor = new RichEditor();
        outputEditor.setEditable(false);

        styleChooser = new StyleChooser();
        styleChooser.setVisible(false);

        outPanel.add(outputEditor, BorderLayout.CENTER);
        outPanel.add(styleChooser, BorderLayout.SOUTH);

        // 内容区域输入
        final JPanel inputPanel = new JPanel(new BorderLayout(0, 0));
        inputPanel.setOpaque(false);

        // 工具条
        final JPanel midToolBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 3, 3));
        midToolBar.setBackground(DEFAULT_MID_TOOLBAR_BACKGROUND);

        styleBtn = new JToggleButton(styleButtonIcon, false);
        styleBtn.setBorder(null);
        styleBtn.setContentAreaFilled(false);
        styleBtn.setFocusPainted(false);

        faceBtn = new JToggleButton(faceButtonIcon, false);
        faceBtn.setBorder(null);
        faceBtn.setContentAreaFilled(false);
        faceBtn.setFocusPainted(false);

        helpBtn = new JButton(" ? ");
        helpBtn.setBorder(null);
//        helpBtn.setContentAreaFilled(false);
        helpBtn.setFocusPainted(false);
        helpBtn.setMargin(new Insets(10, 10, 10, 10));

        midToolBar.add(styleBtn);
        midToolBar.add(faceBtn);
        midToolBar.add(helpBtn);

        inputEditor = new RichEditor();

        inputPanel.add(midToolBar, BorderLayout.NORTH);
        inputPanel.add(inputEditor, BorderLayout.CENTER);

        contentPane.add(outPanel, JSplitPane.TOP);
        contentPane.add(inputPanel, JSplitPane.BOTTOM);

        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splitPane.setOneTouchExpandable(true);
//        splitPane.setResizeWeight(0.95);
//        splitPane.setEnabled(false);    // 禁用拖动
        splitPane.setResizeWeight(0.7);
        splitPane.setDividerSize(0);
        splitPane.add(contentPane, JSplitPane.LEFT);
        if (peer != null && peer instanceof BuddyPair) {
            BuddyPair pair = (BuddyPair) peer;
            /*
            JList memberList = new JList(pair.getMembers());
            memberList.setBorder(
                    BorderFactory.createTitledBorder(
                            BorderFactory.createLineBorder(new Color(130, 150, 160), 1),
                            "成员列表",
                            TitledBorder.CENTER,
                            TitledBorder.TOP
                    )
            );
            */
            Buddy[] members = pair.getMembers();
            Buddy[][] buddies = new Buddy[members.length][1];
            for (int i = 0; i < members.length; i++) {
                buddies[i][0] = members[i];
            }
            DefaultTableModel model = new DefaultTableModel(buddies, new String[] {"成员列表"}){
                // 为了可以选择不可以编辑
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false;
                }
            };
            JTable memberList = new JTable(model);
            memberList.setDragEnabled(false);
//            memberList.setEnabled(false);
            JScrollPane scrollPane = new JScrollPane(memberList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            scrollPane.setOpaque(false);
            scrollPane.getViewport().setOpaque(false);
            splitPane.add(scrollPane, JSplitPane.RIGHT);
            splitPane.setOpaque(false);
            splitPane.setDividerSize(5);
        }

        this.add(avatarPanel, BorderLayout.NORTH);
//        this.add(contentPane, BorderLayout.CENTER);
        this.add(splitPane, BorderLayout.CENTER);
        this.setBackground(new Color(0xF4F9FC));

        faceChooser = new FaceChooser();

        handler = new Handler();

        styleBtn.addItemListener(handler);
        faceBtn.addItemListener(handler);
        helpBtn.addActionListener(handler);
        styleChooser.addPropertyChangeListener(handler);
        faceChooser.addActionListener(handler);
        inputEditor.getTextPane().addKeyListener(handler);

        this.setPreferredSize(new Dimension(400, 300));
    }

    public T getChatPeer() {
        return this.peer;
    }

    @Override
    // todo --------
    public void writeMessage(AbstractPollReply msg) {
        onMessage(msg);
    }

    /**
     * 发送 / 接收消息处理
     */
    private void sendMessage() {
        List<RichElement> content = inputEditor.getContent();

        // 内容输出
        String sendTime = new SimpleDateFormat(DATE_FORMAT).format(new Date());
        new Message("自己 " + sendTime, content).appendTo(outputEditor);
        outputEditor.appendParagraphEnd();
        inputEditor.clearContent();

        // 内容发送
        SendableMessage sendableMessage = WebQQClient.getCurrentClient().createSendableMessage();
        sendableMessage.setMessageStyle(convertStyle(inputEditor.getDefaultStyle()));
        for (RichElement e : content) {
            sendableMessage.addElement(convertElement(e));
        }
        doSend(sendableMessage, peer.getId());
    }

    protected abstract void doSend(SendableMessage msg, long id);

    /**
     * 接收消息
     *
     * @param msg
     */
    public synchronized void onMessage(AbstractPollReply msg) {
        // TODO 获取消息头
        if (msg == null) {
            return;
        }

        String header = getSenderName(msg) + " " + new SimpleDateFormat(DATE_FORMAT).format(new Date(msg.getTime() * 1000));
        org.vacoor.xqq.core.msg.Message message = msg.getMessage();
        Iterable<RichElement> richElements = convertElements(message, message.getMessageStyle());
        new Message(header, richElements, Color.BLUE).appendTo(outputEditor);
        outputEditor.appendParagraphEnd();
    }

    protected String getSenderName(AbstractPollReply msg) {
        String name = peer.getName();
        String mark = peer.getMark();
        if (mark != null && mark.length() > 0) {
            name = mark;
        }
        return name;
    }

    /**
     * 请求 Input 获取输入焦点
     */
    public void requestFocusForInput() {
        inputEditor.getTextPane().requestFocus();
    }


    protected void setHelpText(String text) {
        this.helpText = text;
    }

    /**
     * 事件处理 --------------------------------------------------------------------
     */
    protected class Handler extends KeyAdapter implements PropertyChangeListener, ActionListener, ItemListener {
        // 快捷键
        @Override
        public void keyPressed(KeyEvent e) {
            // 如果不是 ALT + S
            if (!(e.isAltDown())) {
                return;
            }

            if (KeyEvent.VK_A == e.getKeyCode()) {
                styleBtn.setSelected(!styleBtn.isSelected());
                return;
            }

            if (!(e.isAltDown() && KeyEvent.VK_S == e.getKeyCode())) {
                return;
            }

            sendMessage();
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

        /**
         * 样式 / 表情 选择按钮处理
         */
        @Override
        public void itemStateChanged(ItemEvent e) {
            if (styleBtn == e.getSource()) {
                styleChooser.setVisible(ItemEvent.SELECTED == e.getStateChange());
                return;
            }

            if (faceBtn == e.getSource()) {
                if (ItemEvent.SELECTED == e.getStateChange()) {
                    Point f = faceBtn.getParent().getLocationOnScreen();
                    int width = faceChooser.getWidth();
                    int height = faceChooser.getHeight();

                    faceChooser.setLocation(f.x - width / 2, f.y - height);
                    faceChooser.setVisible(true);
                } else {
                    faceChooser.setVisible(false);
                }
            }
        }

        /**
         * 样式选择处理
         */
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            String prop = evt.getPropertyName();
            Object newValue = evt.getNewValue();
            Style style = inputEditor.getDefaultStyle();

            if (StyleChooser.FONT_FAMILY_PROPERTY_NAME.equals(prop)) {
                style.setFontFamily(String.valueOf(newValue));
                return;
            }

            if (StyleChooser.FONT_SIZE_PROPERTY_NAME.equals(prop)) {
                style.setFontSize(((Integer) newValue));
                return;
            }

            if (StyleChooser.BOLD_PROPERTY_NAME.equals(prop)) {
                style.setBold((Boolean) newValue);
                return;
            }

            if (StyleChooser.ITALIC_PROPERTY_NAME.equals(prop)) {
                style.setItalic((Boolean) newValue);
                return;
            }

            if (StyleChooser.UNDERLINE_PROPERTY_NAME.equals(prop)) {
                style.setUnderline((Boolean) newValue);
                return;
            }

            if (StyleChooser.COLOR_PROPERTY_NAME.equals(prop)) {
                style.setColor((Color) newValue);
            }
        }

        /**
         * 表情选择处理
         *
         * @param e
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            if (helpBtn == e.getSource()) {
                JOptionPane.showMessageDialog(null, helpText, "Help", JOptionPane.NO_OPTION);
                return;
            }

            if (FaceChooser.OK_COMMOND == e.getActionCommand()) {
                JLabel label = (JLabel) e.getSource();
                int id = Integer.parseInt(label.getToolTipText().replaceAll("[^0-9]", ""));
                new Face(id, label.getIcon()).appendTo(inputEditor);
                faceBtn.setSelected(false);
                requestFocusForInput();
                return;
            }

            /*-
             * 这里防止循环调用, 因为在鼠标点击 FaceChooser 外本来就会触发 CANCEL_COMMAND, 而 JToggleButton 自身也会触发
             * 结果调用两次 faceBtn.setSelected(false), 由于 JToggleButton 采用 setSelected(!isSelected()) 因此会出错
             */
            if (FaceChooser.CANCEL_COMMOND == e.getActionCommand() && e.getSource() != faceBtn) {
                faceBtn.setSelected(false);
                return;
            }
        }
    }

    /**
     * 转换函数 -----------------------------------------
     */
    protected Iterable<RichElement> convertElements(Iterable<MessageElement> msgElements, MessageStyle messageStyle) {
        Style style = convertStyle(messageStyle);
        List<RichElement> elements = new ArrayList<RichElement>();
        for (MessageElement me : msgElements) {
            AbstractElement element = convertElement(me);
            element.setStyle(style);
            elements.add(element);
        }
        return elements;
    }

    protected AbstractElement convertElement(MessageElement messageElement) {
        AbstractElement element = new Text(messageElement.toString());
        if (messageElement instanceof MessageElement.FaceElement) {
            int faceId = ((MessageElement.FaceElement) messageElement).getId();
            element = new Face(faceId, FaceUtil.faceID2DynamicFace(faceId));
        }

        return element;
    }

    protected SimpleMessage convertElements(Iterable<RichElement> elements, Style style) {
        SimpleMessage simpleMessage = new SimpleMessage();
        simpleMessage.setMessageStyle(convertStyle(style));
        for (RichElement e : elements) {
            simpleMessage.addElement(convertElement(e));
        }
        return simpleMessage;
    }

    protected MessageElement convertElement(RichElement element) {
        MessageElement me = MessageElement.createTextElement(element.toString());
        if (element instanceof Face) {
            me = MessageElement.createFaceElement(((Face) element).getId());
        }
        return me;
    }

    // 消息样式 --> JTextPane 样式
    protected Style convertStyle(MessageStyle messageStyle) {
        return new Style(messageStyle.getFontFamily(), messageStyle.getFontSize() + FONT_OFFSET, messageStyle.isBold(), messageStyle.isItalic(), messageStyle.isUnderline(), messageStyle.getColor());
    }

    protected MessageStyle convertStyle(Style style) {
        return new MessageStyle(style.getFontFamily(), style.getFontSize() - FONT_OFFSET, style.isBold(), style.isItalic(), style.isUnderline(), style.getColor());
    }
}
